//
// Created by wuzhe on 2022/3/30.
//

#include <memory>
#include "Space.hh"
#include "Player.hh"
#include "Console.h"
#include "Reactor.hh"
#include "Slave.h"
#include <vector>
extern unsigned int slaveStartNum;
int currentSlaveNum;
Space::Space() {
    INFO("init space");
    redis = RedisAsync::getSingleton();
    redis->get("roomId", [=](std::string& id){
        if (id == "nil") {
            redis->set("roomId", "1", [=](std::string id){
                id = "1";
                roomIncreaseId = std::stoi(id);
            });
        }
        roomIncreaseId = std::stoi(id);
        if (!isMain) {
            return;
        }
        // 获取roomId列表
        getroomNameList();
    });
}

/**
 * slave启动完毕后，将房间编号发放给slave server
 */
void Space::onSlaveBoot() {
    int n = 0;
    auto itr = roomNameList.begin();
    std::vector<Slave*> slaveVector{};
    for (auto &[k,v] : slaveMap) {
        slaveVector.push_back(v);
    }
    while (itr != roomNameList.end()) {
        std::string roomName = *itr;
        int slaveId = n % slaveStartNum;
        slaveVector[slaveId]->sendRoomId(roomName);
        roomName2Slave[roomName] = slaveVector[slaveId];
        itr++;
        n++;
    }
}

void Space::createRoom(const std::string& roomName, Player* player) {
    std::string key("room:");
    key += roomName;
    redis->find(key, [=, &player](auto list) {
        for (std::string &a : list) {
            return nullptr;
            break;
        }
        // todo create room
        redis->set(key, std::to_string(roomIncreaseId), [=](auto res) {
            roomIncreaseId++;
            redis->set("roomId", std::to_string(roomIncreaseId), [=](auto res) {
//                player->createRoomCB(room);
            });
        });
    });
}

Entity& Space::createEntity(int num, const std::string& IP) {
    if (entityMap.count(IP) == 0) {
        Entity *p = new Entity(num, EntityType::None);
        entityMap[IP] = p;
    }
    return *entityMap[IP];
}

void Space::processDatagram(ParseRes &res, const std::string& IP) {
    UnivHead* head = res.head;
    // slave process
    if (!isMain) {
        processMainResp(res);
        return;
    }
    // main
    Entity *entity = nullptr;
    if ((entity = entityMap[IP]) != nullptr) {

        if (res.head->isSlave == 0x01) {
            auto *slave = new Slave(entity->num);
            currentSlaveNum++;
            slaveMap[IP] = slave;
            slave->process(res);
        } else {
            auto *player = new Player(entity->num);
            playerMap[IP] = player;
            player->process(res);
        }
        delete entity;
        entityMap[IP] = nullptr;
    } else if ((entity = playerMap[IP]) != nullptr) {
        entity->process(res);
    } else if ((entity = slaveMap[IP]) != nullptr) {
        entity->process(res);
    } else {
        std::string content("invalid ip:");
        content += IP;
        errLog(content);
        return;
    }
    if (slaveStartNum == currentSlaveNum && !isInit) {
        onSlaveBoot();
        isInit = true;
    }
}

void Space::send2Client(Player *player, char type, const std::string& content) {
    UnivHead univHead {0};
    univHead.type = type;
    if (player->currentRoom != nullptr) {
        univHead.roomId = player->currentRoom->roomId;
    }
    univHead.length = content.size();
    JJ::sendDatagram(player->num, &univHead, content.c_str());
}

void Space::send2Master(char type, const std::string& content) {
    UnivHead univHead {0};
    univHead.type = type;
    univHead.isSlave = 0x01;
    univHead.length = content.size();
    JJ::sendDatagram(num, &univHead, content.c_str());
}

void Space::send2Slave(Slave *slave, char type, const std::string& content) {
    UnivHead univHead {0};
    univHead.type = type;
    univHead.length = content.size();
    JJ::sendDatagram(slave->num, &univHead, content.c_str());
}

void Space::sendRoomList(Player *player, std::string filter) {
   std::stringstream ss;
   for (auto& [roomId, roomName] : roomId2nameMap) {
       ss << roomName << ":" << roomId << std::endl;
   }
    send2Client(player, ROOM_LIST2, ss.str());
}

void Space::onEnterRoom(Player *player, int roomId) {
    if (isMain) {

    } else {
        Room *room = roomMap[roomId];
        std::cout << room->roomName << std::endl;
        room->onPlayerEnter(player);
        // todo
    }
}

void Space::getroomNameList() {
    redis->find("room:*", [=](std::list<std::string> list) {
        for (std::string a : list) {
            roomNameList.push_back(a);
            redis->get(a, [=](std::string id) {
                roomId2nameMap[std::stoi(id)] = a;
            });
        }
    });
}

void Space::onExitRoom(Player *player, int roomId) {
    if (isMain) {
        // todo
    } else {
        Room *room = roomMap[roomId];
        room->onPlayerQuit(player);
    }
}

void Space::processDisconnect(const std::string& IP) {
    Entity *entity = nullptr;
    if ((entity = entityMap[IP]) != nullptr) {
        this->entityMap.erase(IP);
    } else if ((entity = playerMap[IP]) != nullptr) {
        this->playerMap.erase(IP);
    } else if ((entity = slaveMap[IP]) != nullptr) {
        this->slaveMap.erase(IP);
    } else {
        std::string content("invalid ip:");
        content += IP;
        errLog(content);
        return;
    }
    delete entity;
}

void Space::recordMsg(Room *room, const Msg& msg) {
    std::stringstream ss;
    ss << "roomMsg:" << room->roomId << ":" << msg.time;
    redis->set(ss.str(), msg.toString(), [=](std::string&){});

}

void Space::updatePlayerInfo(Player *player) {
    std::stringstream ss;
    ss << "player:" << player->getRoleName();
    redis->set(ss.str(), player->toString(), [=](std::string&){});
}

void Space::onExit(Player *player) {
    std::stringstream ss;
    ss << "space:Player\"" << player->getRoleName() << "\" exited";
    INFO(ss.str());
    ss.clear();
    ss.str("");
    ss << "player:" << player->getRoleName();
    redis->del(ss.str(), [=](std::string res) {
        JJ::playerDisconnect(player->num);
    });
}

void Space::getPlayerInfo(Player *player, std::function<void(std::string)> cbFunc) {
    std::stringstream ss;
    ss << "player:" << player->getRoleName();
    redis->get(ss.str(), cbFunc);
}

void Space::initRoomHistory(Room *room, int count) {
    std::stringstream ss;
    ss << "roomMsg:" << room->roomId << ":*";
    redis->find(ss.str(), [=](auto tmp) {
        tmp.sort([](std::string& a, std::string& b) {
            return a > b;
        });
        int k = 0;
        for(auto & str : tmp) {
            if (k++ == count) {
                break;
            }
            redis->get(str, [=](const std::string& val){
                room->msgQueue.push_back(val);
            });
        }
    });
}
// 针对slave server
void Space::processMainResp(ParseRes &res) {
    slave->process(res);
}

int Space::getNum() {
    return slave->num;
}

void Space::setNum(int num) {
    if (slave == nullptr) {
        slave = new Slave(num);
    } else {
        errLog("SLAVE SHOULD NOT NEW TWICE!");
    }
}

void Space::instRoom(std::string roomName, int roomId) {
    roomMap[roomId] = new Room(this, roomName.substr(5), roomId);
}
